
#include <sstream>
#include <assert.h>
#include <exception>
#include "GraphTools.h"
#include "rapidjson/document.h"
#include "rapidjson/filereadstream.h"

using namespace rapidjson;

char currentProjectPath[300]="./";

void errMsg(const char* str)
{

	sprintf(resultpath, "%s/error.txt", currentProjectPath);
	FILE* file = fopen(resultpath, "wb");
	fprintf(file, str);
	fclose(file);
}

char resultpath[200];
void write2Log(const char * str)
{
#ifndef _DEBUG
	return; // this is release mode
#endif
			//printf(str);
	sprintf(resultpath, "%s/df_log.txt", currentProjectPath);
	FILE * output = fopen(resultpath, "ab");
	fprintf(output, str);
	fclose(output);
}

////////////////////////////////
///
///  find n-th '\n
///
////////////////////////////////
int find_nth_endl(const string& str, int startPostion, int n)
{
	int num = 0;
	int pos = startPostion;

	while (num<n)
	{
		int t = pos;
		pos = str.find('\n', pos + 1);
		if (pos == string::npos)
			return -1;
		num++;
	}
	return pos;
}


////////////////////////////////
///////   DDA Data tools
////////////////////////////////

DDAPoint::DDAPoint(double x, double y, int blockNo)
{
	this->x = x;
	this->y = y;
	this->blockNo = blockNo;
}

FixedPoint::FixedPoint(double x, double y, int blockNo) :DDAPoint(x, y, blockNo) {}
FixedPoint::FixedPoint() : DDAPoint() {}

LoadingPoint::LoadingPoint(double x, double y, int blockNo) : DDAPoint(x, y, blockNo) {}
LoadingPoint::LoadingPoint() : DDAPoint() {}

MeasuredPoint::MeasuredPoint(double x, double y, int blockNo) : DDAPoint(x, y, blockNo) {}
MeasuredPoint::MeasuredPoint() : DDAPoint() {}

void FrameData::reset()
{
	blocks.clear();
	fixedPoints.clear();
	loadingPoints.clear();
	measuredPoints.clear();
}


/////////////////////////////////////////////////////////
/////       
/////	FrameDataTaker
/////
/////////////////////////////////////////////////////////
//FrameData FrameDataTaker::_nullFrameData;
//
//FrameDataTaker::FrameDataTaker()
//{
//	_graph = new FrameData();
//	_frameReader = new FrameReader();
//}
//
//FrameDataTaker::~FrameDataTaker()
//{
//	if (_graph) { delete _graph; _graph = NULL; }
//	if (_frameReader) { delete _frameReader; _frameReader = NULL; }
//}
//
//const vector<int> FrameDataTaker::getSchema()
//{
//	return _frameReader->getSchema();
//}
//const vector<double> FrameDataTaker::getWindowInfo()
//{
//	return _frameReader->getWindowInfo();
//}
//
//bool FrameDataTaker::setFile(char* filename)
//{
//	return _frameReader->setFile(filename);
//}
//
//bool FrameDataTaker::setStepInterval(int stepInterval)
//{
//	//_frameReader-
//	return true;
//}
//void FrameDataTaker::setCurrentFrameIdx(int frameIdx)
//{
//	bool res = _frameReader->setCurrentFrameIndex(frameIdx);
//	if (!res) throw std::runtime_error("Unvalid data");
//}
//
//FrameData& FrameDataTaker::getNextGraph()
//{
//	const string frame = _frameReader->getNextFrame();
//	if (!frame.size()>2) throw std::runtime_error("Unvalid data");
//	if (frame.size()<2)  // no file any more
//	{
//		return FrameDataTaker::_nullFrameData;
//	}
//
//	const vector<int> schema = _frameReader->getSchema();
//	int pos = 0;
//
//	// parse blocks schema
//	initBlocksSchema();
//
//	// parse blocks vertices 
//	{
//		pos = find_nth_endl(frame, 0, schema[5] + 1);
//		assert(pos != -1);
//		if (pos == -1) return FrameDataTaker::_nullFrameData;
//		parseBlocksVertices(frame.substr(0, pos + 1));
//	}
//
//	// parse points data
//	{
//		int tmpPos = pos + 1;
//		pos = find_nth_endl(frame, pos + 1, (schema[1] + schema[2] + schema[3]) * 2);
//		assert(pos != -1);
//		if (pos == -1) return FrameDataTaker::_nullFrameData;
//		parsePoints(frame.substr(tmpPos, pos - tmpPos + 1));
//	}
//
//	// parse bolt elements
//	{
//		int tmpPoint = pos + 1;
//		pos = find_nth_endl(frame, pos, schema[4] * 2);
//		assert(pos != -1);
//		if (pos == -1) return FrameDataTaker::_nullFrameData;
//		parseBoltElements(frame.substr(tmpPoint, pos - tmpPoint + 1));
//	}
//
//
//	// parse block stress
//	{
//		int tmpPos = pos + 1;
//		pos = find_nth_endl(frame, pos + 1, schema[0]);
//		parseBlocksStress(frame.substr(tmpPos, pos - tmpPos + 1));
//	}
//
//	// real time for this step
//	{
//		int tmpPos = pos + 1;
//		pos = find_nth_endl(frame, pos + 1, 1);
//		parseTimeAndDisplacement(frame.substr(tmpPos, pos - tmpPos + 1));
//	}
//
//	return *_graph;
//}
//
//bool FrameDataTaker::initBlocksSchema()
//{
//	const vector<int> schema = _frameReader->getSchema();
//	const string blocksInfo = _frameReader->getBlocksSchema();
//	int num = 0;
//	int pos = 0;
//
//	while (pos <= blocksInfo.size())
//	{
//		pos = blocksInfo.find('\n', pos);
//		if (pos == string::npos) break;
//		else
//		{
//			num++;
//			pos++;
//		}
//	}
//	if (!(num == schema[0])) throw std::runtime_error("UnValid data");
//	if (num != schema[0]) return false;
//
//
//#ifdef DEBUGFRAMEDATATAKER
//	char tmpStr[200];
//	sprintf(tmpStr, "record num : %d , blocks num : %d\n", num, schema[0]);
//	msg(tmpStr);
//#endif
//
//	_graph->blocks.resize(schema[0]);
//	_graph->bolts.resize(schema[4]);
//	parseBlocksSchema(blocksInfo);
//	return true;
//}
//
//bool FrameDataTaker::parseBlocksSchema(const string& blocksData)
//{
//	if (!(blocksData.size()>0)) throw std::runtime_error("Unvalid data");
//	vector<Block>& blocks = _graph->blocks;
//	istringstream dataIn(blocksData);
//	int blockIdx = 0;
//	int t1, t2;
//	char str[200];
//
//	msg("start to parse blocks schema.\n");
//	while (blockIdx<blocks.size())
//	{
//		if (dataIn.eof()) throw std::runtime_error("Unvalid data");
//		if (dataIn.eof()) return false;
//
//		dataIn >> t1;
//		dataIn >> t2;
//
//#ifdef DEBUGFRAMEDATATAKER
//		sprintf(str, "%d %d\n", t1, t2);
//		msg(str);
//#endif	
//
//		blocks[blockIdx].startNo = t1;
//		blocks[blockIdx].endNo = t2;
//		blockIdx++;
//	}
//	msg("blocks schema parsed done.\n");
//	return true;
//}
//
//bool FrameDataTaker::parseBlocksVertices(const string& blocksData)
//{
//	//     msgString(blocksData);
//	if (!(blocksData.size()>5)) throw std::runtime_error("Unvalid data");
//
//	istringstream dataIn(blocksData);
//	if (!dataIn.good()) throw std::runtime_error("Unvalid data");
//	vector<Block>& blocks = _graph->blocks;
//
//	char tmpStr[200];
//	int tmpNo;
//	double t1, t2;
//	dataIn >> tmpStr >> t1; // <<<step>>> n
//	_graph->frameNo = int(t1);
//
//	msg(tmpStr);
//	sprintf(tmpStr, "start to parse blocks vertices for frame index : %d.\n", int(t1));
//	msg(tmpStr);
//	for (int t = 0; t<blocks.size(); t++)
//	{
//		if (dataIn.eof()) throw std::runtime_error("Unvalid data");
//		Block& block = blocks[t];
//		int size = block.endNo - block.startNo + 1;
//		block.vertices.resize(size);
//		block.verticesJoints.resize(size);
//		for (int i = 0; i<size; i++)
//		{
//			//             sprintf(tmpStr , "frame Index : %d , vertex No. %d\n" ,t , i );
//			//             msg(tmpStr);
//			if (dataIn.eof()) throw std::runtime_error("Unvalid data");
//			if (dataIn.eof()) return false;
//			dataIn >> tmpNo >> t1 >> t2;
//			block.verticesJoints[i] = tmpNo;
//			block.vertices[i].first = t1;
//			block.vertices[i].second = t2;
//
//#ifdef DEBUGFRAMEDATATAKER
//			sprintf(tmpStr, "%lf %lf\n", t1, t2);
//			msg(tmpStr);
//#endif		
//		}
//
//		// the next is the parameters for the block
//		for (int i = 0; i<4; i++)
//		{
//			if (dataIn.eof()) throw std::runtime_error("Unvalid data");
//			if (dataIn.eof()) return false;
//			dataIn >> tmpNo >> t1 >> t2;
//			block.parameters[2 * i] = t1;
//			block.parameters[2 * i + 1] = t2;
//		}
//
//#ifdef DEBUGFRAMEDATATAKER
//		const double* paras = block.parameters;
//		sprintf(tmpStr, "%lf %lf\n%lf %lf\n%lf %lf\n%lf %lf\n", paras[0], paras[1], paras[2], paras[3]
//			, paras[4], paras[5], paras[6], paras[7]);
//		msg(tmpStr);
//#endif	
//
//	}
//	msg("block vertices parsed done.\n");
//	return true;
//}
//
//bool FrameDataTaker::parseBoltElements(const string& boltElements)
//{
//	istringstream dataIn(boltElements);
//	vector<BoltElement>& bolts = _graph->bolts;
//
//	char tmpStr[200];
//	msg("start to parse bolt elements.\n");
//	for (int i = 0; i<bolts.size(); i++)
//	{
//		double t1, t2, t3;
//		BoltElement& bolt = bolts[i];
//		if (dataIn.eof()) throw std::runtime_error("Unvalid data");
//
//		if (dataIn.eof()) return false;
//		dataIn >> t1 >> t2 >> t3;
//		bolt.startPoint.first = t1;
//		bolt.startPoint.second = t2;
//		bolt.endPoint.first = t3;
//
//		dataIn >> t1 >> t2;
//		bolt.endPoint.second = t1;
//		bolt.pretension = t2;
//
//#ifdef DEBUGFRAMEDATATAKER
//		sprintf(tmpStr, "%lf %lf %lf\n", t1, t2, t3);
//		msg(tmpStr);
//#endif	
//	}
//	msg("bolt elements parsed done.\n");
//	return true;
//}
//
//
//bool FrameDataTaker::parseBlocksStress(const string& blocksStress)
//{
//	istringstream dataIn(blocksStress);
//	vector<Block>& blocks = _graph->blocks;
//
//	char tmpStr[200];
//	msg("start to parse blocks vertices.\n");
//	for (int t = 0; t<blocks.size(); t++)
//	{
//		double t1, t2, t3;
//		Block& block = blocks[t];
//		if (dataIn.eof()) throw std::runtime_error("Unvalid data");
//		if (dataIn.eof()) return false;
//		dataIn >> t1 >> t2 >> t3;
//		block.stressX = t1;
//		block.stressY = t2;
//		block.stressXY = t3;
//
//#ifdef DEBUGFRAMEDATATAKER
//		sprintf(tmpStr, "%lf %lf %lf\n", t1, t2, t3);
//		msg(tmpStr);
//#endif	
//
//	}
//	msg("block stress parsed done.\n");
//	return true;
//}
//
//bool FrameDataTaker::parseTimeAndDisplacement(const string& timeAndDisplacement)
//{
//	istringstream dataIn(timeAndDisplacement);
//
//	dataIn >> _graph->maxDisplacement;
//	dataIn >> _graph->totalTime;
//	dataIn >> _graph->internalTime;
//	return true;
//}
//
//
//bool FrameDataTaker::parse1Point(istringstream& dataIn, DDAPoint& point)
//{
//	double t1, t2, t3, t4, t5;
//	if (dataIn.eof())throw std::runtime_error("Unvalid data");
//	if (dataIn.eof()) return false;
//	dataIn >> t1 >> t2 >> t3 >> t4 >> t5;
//	point.x = t1;
//	point.y = t2;
//	point.blockNo = int(t3);
//	point.speedX = t4;
//	point.speedY = t5;
//
//
//#ifdef DEBUGFRAMEDATATAKER
//	char tmpStr[200];
//	sprintf(tmpStr, "%lf %lf %lf\n%lf %lf\n", t1, t2, t3, t4, t5);
//	msg(tmpStr);
//#endif
//
//	return true;
//}
//
//bool FrameDataTaker::parsePoints(const string& pointsData)
//{
//	istringstream dataIn(pointsData);
//	const vector<int> schema = _frameReader->getSchema();
//
//	char tmpStr[200];
//	msg("start to parse points.\n");
//
//	{
//		// fixed points
//		vector<FixedPoint>& fps = _graph->fixedPoints;
//		_graph->fixedPoints.resize(schema[1]);
//		for (int i = 0; i<schema[1]; i++)  // schema[1], fixed points num
//		{
//			if (dataIn.eof())throw std::runtime_error("Unvalid data");
//			if (dataIn.eof()) return false;
//			if (!parse1Point(dataIn, fps[i])) return false;
//		}
//		sprintf(tmpStr, "%d fixed points parsed done.\n", schema[1]);
//		msg(tmpStr);
//	}
//
//	{
//		// loading points
//		vector<LoadingPoint>& lps = _graph->loadingPoints;
//		_graph->loadingPoints.resize(schema[2]);
//		for (int i = 0; i<schema[2]; i++)  // schema[2], loading points num
//		{
//			if (dataIn.eof()) throw std::runtime_error("Unvalid data");
//			if (dataIn.eof()) return false;
//			if (!parse1Point(dataIn, lps[i])) return false;
//		}
//		sprintf(tmpStr, "%d loading points parsed done.\n", schema[2]);
//		msg(tmpStr);
//	}
//
//	{
//		// measured points
//		vector<MeasuredPoint>& mps = _graph->measuredPoints;
//		_graph->measuredPoints.resize(schema[3]);
//		for (int i = 0; i<schema[3]; i++)  // schema[3], measured points num
//		{
//			if (dataIn.eof()) throw std::runtime_error("Unvalid data");
//			if (dataIn.eof()) return false;
//			if (!parse1Point(dataIn, mps[i])) return false;
//		}
//
//		// 		double t1 , t2 , t3 , t4 , t5 , t6;
//		// 		for(int i=0; i<schema[3];i++)
//		// 		{
//		// 			assert(!dataIn.eof());
//		// 			if (dataIn.eof()) return false;
//		// 			dataIn>>t1>>t2>>t3>>t4>>t5>>t6;
//		// 			mps[i].u=t1;
//		// 			mps[i].v=t2;
//		// 			mps[i].r=t3;
//		// 			mps[i].stressX=t4;
//		// 			mps[i].stressY=t5;
//		// 			mps[i].stressXY=t6;
//		// 		}
//		sprintf(tmpStr, "%d measured points parsed done.\n", schema[3]);
//		msg(tmpStr);
//	}
//	return true;
//}
//

bool exportFrameData(FrameData& frame)
{
	char fileName[300];
	sprintf(fileName, "%s/data_%d.df", currentProjectPath, frame.frameNo);
	FILE* tmpFile = fopen(fileName, "wb");
	if (!tmpFile)
	{
		char msg[300];
		sprintf(msg, "export file \"%s\" open failed.", fileName);
		throw std::runtime_error(msg);
	}

	vector<Block>& blocks = frame.blocks;
	int nums = 0;
	for (int i = 0; i<blocks.size(); i++)
		nums += blocks[i].vertices.size() + 4;
	fprintf(tmpFile, "%d %d %d\n %d %d %d\n", frame.blocks.size() \
		, frame.bolts.size(), nums, frame.fixedPoints.size() \
		, frame.loadingPoints.size(), frame.measuredPoints.size());


	for (vector<Block>::iterator it = blocks.begin(); it != blocks.end(); it++)
		fprintf(tmpFile, "%d %d %d\n", it->blockIdx, it->startNo, it->endNo);

	for (vector<Block>::iterator it = blocks.begin(); it != blocks.end(); it++)
	{
		for (int i = 0; i<it->vertices.size(); i++)
			fprintf(tmpFile, "%d %lf %lf\n", it->verticesJoints[i] \
				, it->vertices[i].first, it->vertices[i].second);
	}

	for (vector<BoltElement>::const_iterator it = frame.bolts.begin(); \
		it != frame.bolts.end(); it++)
		;
	// fprintf("%lf %lf %lf %lf %d %d %lf %lf %lf\n" , );
	return true;
}


void JointManager::updateJoints(double waterLevel, std::vector<std::vector<double> >& joints)
{
	if (0 == _jntsByWater.count(waterLevel)) {
		char msg[500];
		sprintf(msg, "water level %lf not found.\n", waterLevel);
		errMsg(msg);
		exit(0);
	}
	Strings jntNames = _jntsByWater[waterLevel];
	assert(jntNames.size()+1 == joints.size()); // !!! very important !!!
	string name;
	string log = "load joints by water level :"+std::to_string(waterLevel);
	char msg[200];
	for (int i = 0; i < jntNames.size(); ++i) {
		name = jntNames[i];
		Joint& jnt = _jnts[name];
		std::vector<double>& arr = joints[i+1];
		arr[0] = jnt.fri;
		arr[1] = jnt.coh;
		arr[2] = jnt.ten;

		sprintf(msg, "\t%s <f:%lf, c:%lf, t:%lf>\n", name.c_str(), arr[0], arr[1], arr[2]);
		log += msg;
	}
	write2Log(log.c_str());
}

void loadJsonParas(const std::string& filename, JointManager& jntMgr, WaterParameterManager& wpMgr
	, bool& updateJntsByTime, int& jntsNum, int& savePerSteps)
{
	FILE* fp = fopen(filename.c_str(), "rb"); // use "r" in non-Windows platform, i.e., linux
	char readBuffer[65536];
	FileReadStream is(fp, readBuffer, sizeof(readBuffer));
	Document d;
	d.ParseStream(is);


	//////////////////////////////////////////////
	/// global parameters
	//////////////////////////////////////////////
	if (0 == strcmp("time", d["waterBy"].GetString())) {
		updateJntsByTime = true;
	}
	savePerSteps = d["savePerSteps"].GetInt();

	//////////////////////////////////////////////
	/// water
	//////////////////////////////////////////////
	WaterParameterManager::WaterLevels& wls = wpMgr.getLevels();
	Value& water  = d["waterLevels"];
	Value& keys   = water["keys"];
	Value& levels = water["levels"];
	for(int i=0; i<keys.Size(); ++i)
		wls.push_back(std::pair<double, double> (keys[i].GetDouble(), levels[i].GetDouble()));

	//////////////////////////////////////////////
	/// joints
	//////////////////////////////////////////////
	jntsNum = d["jointNumInCmpt"].GetInt();

	std::map<std::string, Joint>& jnts = jntMgr.getJoints();
	std::map<double, Strings>& jntConfig = jntMgr.getJntConfigs();

	for (auto& v : d["jointsLib"].GetObject()) {
		Value& jnt = v.value;
		jnts[v.name.GetString()] = Joint(jnt["f"].GetDouble(), jnt["c"].GetDouble(), jnt["t"].GetDouble());
	}


	Value& jwc = d["waterJointConfig"];
	if (jwc.Size() != wls.size()) {
		char msg[300];
		sprintf(msg, "number of \"waterLevel\" and \"jointWaterConfig\"are not equal. (%d vs. %d)\n", wls.size(), jwc.Size());
		errMsg(msg);
		exit(0);
	}
	int i = 0;
	for(auto& jsonJntNames: jwc.GetArray()){
		Strings& jntNames = jntConfig[wls[i].second];
		for (auto& name : jsonJntNames.GetArray()) {
			jntNames.push_back(name.GetString());
		}
		++i;
	}

	fclose(fp);


	//////////////////////////////////////////////
	/// output
	//////////////////////////////////////////////
	FILE* file = fopen("extraCofnig.log", "wb");
	fprintf(file, "savePerSteps: %d\n", savePerSteps);
	fprintf(file, "update water time: %s\n", updateJntsByTime?"true":"false");
	fprintf(file, "number of levels: %d\n\t", wls.size());
	for (auto& kv : wls) 
		fprintf(file, " <%lf, %lf>", kv.first, kv.second);
	fprintf(file, "\nnumber of joints: %d\n", jntsNum);
	fclose(file);
}
